package com.example.wowangz.redis.lock.arithmetic;


import com.alibaba.dubbo.common.utils.StringUtils;
import com.example.wowangz.redis.RedisUtil;
import com.example.wowangz.redis.lock.exception.RedisLockException;

import java.io.Serializable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
 * 简单的使用原生redis实现的分布式锁,分布式的redis会出现锁不住的情况
 */
public class RedisLock implements Lock, Serializable {
    /**
     * serialVersionUID
     */
    private static final long serialVersionUID = 6278513934629913285L;

    private String key;

    /**
     * 超时时间，以毫秒为单位
     * 默认为3分钟
     */
    private long overtime = 3 * 60 * 1000L;

    /**
     * 休眠时长，以毫秒为单位
     * 默认为100毫秒
     */
    private long sleepTime = 100L;

    /**
     * 当前时间
     */
    private long currentLockTime;

    public RedisLock(String key) {
        this.key = key;
    }

    /* (non-Javadoc)
     * @see java.util.concurrent.locks.Lock#lock()
     */
    @Override
    public synchronized void lock() {
        while (true) {
            // 当前加锁时间
            currentLockTime = System.currentTimeMillis();
            if (RedisUtil.setnx(key, String.valueOf(currentLockTime)) > 0) {
                // 获取锁成功
                return;
            } else {
                //其他线程占用了锁
                String value = RedisUtil.get(key);
                Long otherLockTime = StringUtils.isBlank(value) ? null : Long.valueOf(value);
                if (otherLockTime == null) {
                    // 其他系统释放了锁
                    // 立刻重新尝试加锁
                    continue;
                } else {
                    if (currentLockTime - otherLockTime >= overtime) {
                        //锁超时
                        //尝试更新锁
                        String newValue = RedisUtil.getset(key, String.valueOf(currentLockTime));
                        Long otherLockTime2 = StringUtils.isBlank(newValue) ? null : Long.valueOf(newValue);
                        if (otherLockTime2 == null || otherLockTime.equals(otherLockTime2)) {
                            return;
                        } else {
                            sleep();
                            //重新尝试加锁
                            continue;
                        }
                    } else {
                        //锁未超时
                        sleep();
                        //重新尝试加锁
                        continue;
                    }
                }
            }
        }
    }

    /* (non-Javadoc)
     * @see java.util.concurrent.locks.Lock#unlock()
     */
    @Override
    public synchronized void unlock() {
        RedisUtil.del(key);
    }

    /* (non-Javadoc)
     * @see java.util.concurrent.locks.Lock#lockInterruptibly()
     */
    @Override
    public void lockInterruptibly() throws InterruptedException {
        throw new UnsupportedOperationException("方法未实现");
    }

    /* (non-Javadoc)
     * @see java.util.concurrent.locks.Lock#tryLock()
     */
    @Override
    public boolean tryLock() {
        throw new UnsupportedOperationException("方法未实现");
    }

    /* (non-Javadoc)
     * @see java.util.concurrent.locks.Lock#tryLock(long, java.util.concurrent.TimeUnit)
     */
    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        throw new UnsupportedOperationException("方法未实现");
    }

    /* (non-Javadoc)
     * @see java.util.concurrent.locks.Lock#newCondition()
     */
    @Override
    public Condition newCondition() {
        throw new UnsupportedOperationException("方法未实现");
    }

    /**
     * 休眠
     */
    private void sleep() {
        try {
            Thread.sleep(sleepTime);
        } catch (InterruptedException e) {
            throw new RedisLockException("线程异常中断", e);
        }
    }
}
